﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CommonLib {
    public class Tracer {
        private Screen screen;
        private Scene scene;

        public Tracer(Screen scr, Scene sce) {
            this.screen = scr;
            this.scene = sce;

        }
        public Screen Screen {
            get { return this.screen; }
        }
        public Primitive findNearest(Ray ray, ref double distance) {
            Primitive nearest = null;

            for (int j = 0; j < scene.CountPrimitive(); j++) {
                Primitive tmp = scene.GetPrimitive(j);
                if (tmp.isIntersect(ray, ref distance) == true)
                    nearest = tmp;
            }
            return nearest;
        }
        public Vector3 estimateDiffuse(Primitive nrst, Vector3 bndry) {
            Vector3 coeff = new Vector3();

            for (int j = 0; j < scene.CountPrimitive(); j++) {
                Primitive light = scene.GetPrimitive(j);
                if (light.Light == false)
                    continue;
                Vector3 lightDir = light.getNormal(bndry);
                Vector3 primNormal = nrst.getNormal(bndry);
                double arg = - (primNormal & lightDir);
                if ((0 < arg) == false)
                    continue;
                coeff += nrst.Material.diffuse * arg * light.Material.color;
            }
            return coeff;
        }
        public Primitive raytrace(Ray ray, ref Vector3 color, ref Vector3 node) {
            Primitive nearest = null;
            double distance = Double.MaxValue;

            nearest = findNearest(ray, ref distance);
            if (nearest == null)
                return nearest;
            if (nearest.Light) {
                color = nearest.Material.color;
                return nearest;
            }

            node = (distance * ray.direction) + ray.position;
            color = nearest.Material.color * estimateDiffuse(nearest, node);
            return nearest;
        }
        public void render() {
            Primitive prim;

            for (int i = 0; i < screen.getSize(); i++) {
                Vector3 color = new Vector3();
                Vector3 node = new Vector3();

                Ray ray = screen.getRay(i);
                prim = raytrace(ray, ref color, ref node);
                if (prim != null) {
                    Vector3 primNormal = prim.getNormal(node);
                    Ray reflRay = new Ray(node + (DoubleTester.Epsilon * ray.direction),
                        ray.direction + (-2.0 * (ray.direction & primNormal) * primNormal) );
                    Vector3 reflColor = new Vector3();
                    Vector3 reflNode = new Vector3();
                    raytrace(reflRay, ref reflColor, ref reflNode);
                    color += prim.Material.reflection * prim.Material.color * reflColor;
                }
                screen.setPixel(i, color);
            }
        }
    }
}
